#include "controller.h"
#include "mainwindow.h"
#include "canvas.h"
#include <QVector2D>
#include <QVector3D>
#include <QDebug>
#include "bernsteinpolyline.h"
#include "util.h"


Controller::Controller() : rightBegin(NULLPOINT), baseIndex(-1)
{
    mw = new MainWindow();
    c = new Canvas();

    mw->setCentralWidget(c);

    mw->showMaximized();

    connect(c, SIGNAL(left(QPoint)),
            this, SLOT(left(QPoint)));

    connect(c, SIGNAL(leftRelease()),
            this, SLOT(leftRelease()));

    connect(c, SIGNAL(right(QPoint)),
            this, SLOT(right(QPoint)));

    connect(c, SIGNAL(rightRelease()),
            this, SLOT(rightRelease()));

    connect(c, SIGNAL(check()),
            this, SLOT(draw()));

    connect(c, SIGNAL(l()),
            this, SLOT(linear()));

    connect(c, SIGNAL(plus()),
            this, SLOT(chaikin()));

    connect(c, SIGNAL(minus()),
            this, SLOT(chaikinSub()));

    connect(c, SIGNAL(s()),
            this, SLOT(super()));
}

void Controller::left(QPoint p)
{
    sampler.push_back(Point3D(p.x(), p.y(), 0.0));
}

void Controller::leftRelease()
{
    curve.setPoints(sampler);
    original = sampler;
    sampler.clear();
    curve.close();

    draw();
}

void Controller::right(QPoint p)
{
    if(baseIndex == -1)
    {
        rightBegin = p;
        baseIndex = indexOfMostClose(QVector2D(p));
        baseBegin = curve[baseIndex];
        base = curve;
        delta = -1.0;
        ready = false;
        return;
    }

    QVector2D  v( p - rightBegin);
    curve[baseIndex] = Point3D(baseBegin.x() + v.x(),
                               baseBegin.y() + v.y(), 0.0);

    modeling();
}

void Controller::rightRelease()
{
    modeling(true);

    rightBegin = NULLPOINT;
    baseIndex = -1;
}

void Controller::draw()
{
    c->clear();

    bool op = c->originalPointsChecked();
    bool cp = c->customizedPointsChecked();

    if(c->originalShowedCkecked())
        c->drawCurvePoligonal(original, QColor(0,0,0), op);
    if(c->customizedShowedCkecked())
        c->drawCurvePoligonal(curve.getPoints(), QColor(0,255,0), cp);

}

void Controller::linear()
{
    curve.lineFilter();
    draw();
}

void Controller::chaikin()
{
    curve.chaikinFilter();
    draw();
}

void Controller::chaikinSub()
{
    curve.chaikinSubDivide();
    draw();
}

void Controller::super()
{
    curve.superSample(0.5);
    draw();
}

int Controller::indexOfMostClose(const QVector2D& p)
{
    int idx;
    double length;
    double min;

    idx = 0;
    min = (p - QVector2D(curve[0].x(), curve[0].y())).length();

    for(uint i = 1; i < curve.size(); ++i)
    {
        length = (p - QVector2D(curve[i].x(), curve[i].y())).length();
        if(length < min)
        {
            idx = i;
            min = length;
        }
    }

    return idx;
}

int Controller::leftIndexFrom(float delta, int idxBase)
{
    for(unsigned int i = 0; i < base.size(); ++i)
    {
        int idx = idxBase + i;

        Vec3 v = (base[idx].toVec3() - base[idx+1].toVec3());

        if(v.norm() > delta)
            return idx+1;

        delta -= v.norm();
    }

    return baseIndex + 1;
}

int Controller::rightIndexFrom(float delta, int idxBase)
{
    for(unsigned int i = 0; i < base.size(); ++i)
    {
        int idx = idxBase - i;

        Vec3 v = (base[idx].toVec3() - base[idx-1].toVec3());

        if(v.norm() > delta)
            return idx-1;

        delta -= v.norm();
    }

    return baseIndex - 1;
}

void Controller::modeling(bool final)
{
    float d = (baseBegin.toVec3() - curve[baseIndex].toVec3()).norm();
    if(d > delta && !ready)
    {
        delta = d;
        lIdx = leftIndexFrom(delta, baseIndex);
        rIdx = rightIndexFrom(delta, baseIndex);
    }else
        ready = true;

    BernsteinPolyLine b;

    QVector<QVector3D> qcp;
    qcp.append(trans(curve[lIdx      ]));
    qcp.append(trans(curve[baseIndex ]));
    qcp.append(trans(curve[rIdx      ]));
    b.bindControlPoints(qcp);
    qcp = b.getPolyLine();
    //qcp = b.getPolyLine(qcp);

    std::vector<int> list;
    for(int i = baseIndex+1; i <= lIdx; ++i)
        list.push_back(i);
    list.push_back(baseIndex);
    for(int i = rIdx; i < baseIndex; ++i)
        list.push_back(i);

    if(!final)
    {
        Curve curve2 = curve;
        int ind = curve2.remove(list);
        for(int i = 0; i < qcp.size(); ++i)
        {
            curve2.insert(ind, trans(qcp[i]));
        }

        draw();
        c->drawCurvePoligonal(curve2.getPoints(), QColor(255,0,0), true);
    }else
    {
        int ind = curve.remove(list);
        for(int i = 0; i < qcp.size(); ++i)
        {
            curve.insert(ind, trans(qcp[i]));
        }

        draw();
    }
}
